package com.ccsoft.gsms.security;


import com.ccsoft.gsms.model.SysPermission;
import com.ccsoft.gsms.service.SysPermissionService;
import com.ccsoft.gsms.utils.JsonResult;
import com.ccsoft.gsms.utils.MD5;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Component;

import java.io.PrintWriter;
import java.util.List;

/**
 * @ClassName:SecurityConfig
 * @Description:TODO
 * @Author:chanchaw
 * @Date:2019-11-27 14:13
 * @Version:1.0
 **/
@Component
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

/*    @Autowired
    private MyAuthenticationSuccessHandler successHandler;

    @Autowired
    private MyAuthenticationFailureHandler failureHandler;*/

    @Autowired
    private SysPermissionService sysPermissionService;

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new PasswordEncoder() {
            @Override
            public String encode(CharSequence rawPassword) {
                return MD5.encode(rawPassword.toString());
            }

            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                String encodePWD = MD5.encode(rawPassword.toString());
                return encodePWD.equals(encodedPassword);
            }
        });
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /*
         * 从数据库权限表中获取所有数据，用于初始化客户端请求所需的权限
         * 如果没有数据则直接退出即可。
         */
        List<SysPermission> list = sysPermissionService.selectAll();
        System.out.println("初始化权限时获取到的权限行数 = " + list.size());
        if(list.size()<=0) return;


//        http.authorizeRequests().antMatchers("/**").fullyAuthenticated()
          http.authorizeRequests()
                .and()
                .formLogin()
                .loginProcessingUrl("/login/in")
                .successHandler(successHandler()).failureHandler(failureHandler())// 认证成功和失败的处理器
                .permitAll()// 登录页面不需要任何权限就可以访问


                .and()
                .logout()
                .logoutSuccessHandler(logoutSuccessHandler())
                .logoutUrl("/login/out")
                .permitAll()// 退出系统的功能也不需要任何权限
                .and().cors()
                .and().csrf().disable();// 禁用 token


        /*
         * 数据库表 sys_permission 中的字段 http_method 表示限定的请求的类型
         * 留空表示不限定请求类型，只匹配请求路径
         * 该字段 = GET ，表示要求同时匹配请求类型为 GET 和请求路径，才有权限访问
         * 下面 FOR 循环中的 IF 用来区分该情况。
         */
        for(SysPermission perm:list){
            if( perm.getNeed() == true ){// 要求权限的请求
                System.out.println(perm.getUrl() + "，要求权限");
                if(perm.getHttp_method()==null||perm.getHttp_method().length()<=0){// 不限定请求类型
                    http.authorizeRequests().antMatchers(perm.getUrl())
                            .hasAnyAuthority(perm.getPermissionid());
                }else{
                    http.authorizeRequests().antMatchers(// 限定请求的类型
                            HttpMethod.resolve(perm.getHttp_method()),perm.getUrl())
                            .hasAnyAuthority(perm.getPermissionid());
                }
            } else{// 不要求权限的请求
                System.out.println(perm.getUrl() + "," + perm.getHttp_method() + "，不要求权限");
                http.authorizeRequests().antMatchers(perm.getUrl()).permitAll();
            }
        }


        http.addFilterAt(customAuthenticationFilter(),
                UsernamePasswordAuthenticationFilter.class);

    }

    // 认证成功的处理器
    private AuthenticationSuccessHandler successHandler() {
        return (request, response, authentication) -> {
            System.out.println("进入认证成功的处理器中！");
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            JsonResult ok = JsonResult.ok("登录成功");
            out.write(objectMapper.writeValueAsString(ok));
            out.flush();
            out.close();
        };
    }

    // 认证失败的处理器
    private AuthenticationFailureHandler failureHandler() {
        return (request, response, authentication) -> {
            System.out.println("进入认证失败的处理器中！");
            System.out.println(authentication);
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            JsonResult error = JsonResult.error("账号或密码错误");
            out.write(objectMapper.writeValueAsString(error));
            out.flush();
            out.close();
        };
    }

    // 登出系统的处理器
    private LogoutSuccessHandler logoutSuccessHandler(){
        return (request, response, authentication) -> {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            if (auth != null){
                new SecurityContextLogoutHandler().logout(request, response, auth);//清除登录认证信息
            }
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            JsonResult ok = JsonResult.ok("注销成功");
            out.write(objectMapper.writeValueAsString(ok));
            out.flush();
            out.close();
        };
    }


    @Bean
    public CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
        CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
        filter.setAuthenticationSuccessHandler(successHandler());
        filter.setAuthenticationFailureHandler(failureHandler());
        filter.setFilterProcessesUrl("/login/in");

        //这句很关键，重用WebSecurityConfigurerAdapter配置的AuthenticationManager，
        // 不然要自己组装AuthenticationManager
        filter.setAuthenticationManager(authenticationManagerBean());
        return filter;
    }



    // 不使用密码
    @Bean
    public static NoOpPasswordEncoder passwordEncoder(){
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }
}
